package com.boboPlanet.comm.filestorage;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.xuyanwu.spring.file.storage.FileInfo;
import cn.xuyanwu.spring.file.storage.FileStorageService;
import cn.xuyanwu.spring.file.storage.recorder.FileRecorder;
import com.alibaba.fastjson.JSON;
import com.boboPlanet.comm.entity.po.FileDetailPO;
import com.boboPlanet.comm.exception.BusinessException;
import com.boboPlanet.comm.feign.PlanetFileApi;
import com.boboPlanet.comm.rabbitmq.PlanetCommMQEnum;
import com.boboPlanet.comm.rabbitmq.PlanetMqProducer;
import com.boboPlanet.comm.result.ResponseResult;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * @Description: TODO 星球文件上传下载模板类  FileRecorder可将文件信息保存到数据库
 * @Author: mis_wu
 * @Date: 2023/7/8
 * @Company planet 特殊下载在微服务自身实现,如bytes下载
 * url : basePath + rootPath + fileName
 **/
@Component
@Slf4j
@RefreshScope
public class PlanetFileStorageTemplate implements FileRecorder {

    private FileStorageService fileStorageService;

    public void setFileStorageService(FileStorageService fileStorageService){
        this.fileStorageService = fileStorageService;
    }

    @Resource
    private PlanetFileApi planetFileApi;

    @Resource
    private PlanetMqProducer producer;

    @Value("${spring.file-storage.local-plus[0].platform}")
    private String platForm;

    @Value("${spring.planet.file.root-path}")
    private String rootPath;

    @Value("${spring.file-storage.local-plus[0].base-path}")
    private String basePath;

    private static final String file_prefix = "planet";

    private static final String SPLIT_STR = "\\.";

    private static final String SPLIT_STR_DIAN = ".";

    /**
     * 星球文件上传
     * @param file 源文件
     * @param fileName 自定义文件名,带后缀，否则无法识别[一般不传，避免重名覆盖文件]
     * @param fileType 文件类型
     * @return FileInfo
     */
    public FileInfo uploadFile(MultipartFile file,String fileName,String fileType){
        if (file == null){
            throw BusinessException.build("上传文件不能为空,请重新上传");
        }
        String originalFilename = file.getOriginalFilename();
        assert originalFilename != null:"源文件名称不能为空,请重新上传或指定文件名";
        String originNameSuffix = originalFilename.substring(file.getOriginalFilename().lastIndexOf(SPLIT_STR_DIAN));
        if (StringUtils.isBlank(fileName)){
            fileName = UUID.randomUUID().toString() + System.currentTimeMillis() + originNameSuffix;
        }
        if (StringUtils.isBlank(fileType)){
            fileType = originNameSuffix.split(SPLIT_STR)[1];
        }

        fileName = file_prefix + fileName;

        FileInfo fileInfo = fileStorageService.of(file)
                //保存到相对路径下，为了方便管理，不需要可以不写
                .setPath(rootPath)
                //关联对象id，为了方便管理，不需要可以不写
                .setObjectId("planet_file")
                //关联对象类型，为了方便管理，不需要可以不写
                .setObjectType("object")
                //保存一些属性，可以在切面、保存上传记录、自定义存储平台等地方获取使用，不需要可以不写
                .putAttr("system","planet")
                .setPlatform(platForm)
                .setSaveFilename(fileName)
                .setContentType(fileType)
                .upload();
        if (ObjectUtils.isEmpty(fileInfo)){
            throw BusinessException.build("文件上传失败,请重新上传或联系管理员");
        }
        //存储
        record(fileInfo);

        return fileInfo;
    }

    /**
     * 星球图片上传API
     * @param imageFile MultipartFile 源图片
     * @param customName String 自定义文件名【带后缀的】
     * @param imgWidth Integer 图片宽度
     * @param imgHeight Integer 图片高度
     * @param smallWidth Integer 略缩图宽度
     * @param smallHeight Integer 略缩图高度
     * @param imageType String 图片类型(后缀)
     * @return FileInfo
     */
    public FileInfo uploadImage(MultipartFile imageFile,String customName,Integer imgWidth,Integer imgHeight,
                                Integer smallWidth,Integer smallHeight,String imageType){
        if (imageFile == null){
            throw BusinessException.build("图片上传失败,图片信息不存在,请重新上传");
        }

        if (StringUtils.isBlank(customName)){
            customName = file_prefix + UUID.randomUUID() + imageFile.getOriginalFilename();
        }
        if (imgWidth == null || imgWidth <= 0){
            imgWidth = 1000;
        }
        if (imgHeight == null || imgHeight <= 0){
            imgHeight = 1000;
        }
        if (smallWidth == null || smallWidth <= 0){
            smallHeight = 1000;
        }
        if (smallHeight == null || smallHeight <= 0){
            smallHeight = 1000;
        }
        if (StringUtils.isBlank(imageType)){
            imageType = Objects.requireNonNull(imageFile.getOriginalFilename())
                    .substring(imageFile.getOriginalFilename().lastIndexOf(SPLIT_STR_DIAN));
        }
        Integer finalImgWidth = imgWidth;
        Integer finalImgHeight = imgHeight;
        Integer finalSmallHeight = smallHeight;
        Integer finalSmallHeight1 = smallHeight;
        FileInfo fileInfo = fileStorageService.of(imageFile)
                .setPath(rootPath)
                .setPlatform(platForm)
                .image(img -> img.size(finalImgWidth, finalImgHeight))
                .thumbnail(th -> th.size(finalSmallHeight, finalSmallHeight1))
                .setSaveFilename(customName)
                .setContentType(imageType)
                .upload();
        if (ObjectUtils.isEmpty(fileInfo)){
            throw BusinessException.build("文件上传失败,请重新上传或联系管理员");
        }
        //保存记录
        record(fileInfo);

        return fileInfo;
    }

    @Override
    public boolean record(FileInfo fileInfo) {
        //存储到数据库
        Map<String, Object> resultMap = FileDetailPO.buildFileInfo(fileInfo);
        if (resultMap.isEmpty() || !(Boolean) resultMap.get(FileDetailPO.MAP_KEY_1)){
            throw BusinessException.build((String) resultMap.get(FileDetailPO.MAP_KEY_2));
        }
        FileDetailPO fileDetailPO = (FileDetailPO) resultMap.get(FileDetailPO.MAP_KEY_3);

        producer.sendMsg(PlanetCommMQEnum.PLANET_SAVE_FILE_MQ, JSON.toJSONString(fileDetailPO));

        fileInfo.setId(fileDetailPO.getId().toString());
        return true;
    }

    /**
     * todo:在使用前需要通过set注入fileStorageService
     * 根据名称获取文件信息[接口本身参数应该是 URL，此处使用fileName]
     * @param fileName String文件名
     * @return FileInfo
     */
    public FileInfo getByUrl(String fileName) {
        //获取数据库信息
        String url = basePath + rootPath + fileName;
        ResponseResult<FileDetailPO> file = planetFileApi.getFileByUrl(url);
        if (file == null || !file.getSuccess()  || file.getData() == null){
            log.error("query file not exist,[getByUrl],param: {}",fileName);
            throw BusinessException.build("查询文件不存在,请检查文件地址是否正确");
        }

        FileInfo fileInfo = BeanUtil.copyProperties(file.getData(),FileInfo.class,"attr");
        //这是手动获取数据库中的 json 字符串 并转成 附加属性字典，方便使用
        if (StringUtils.isNotBlank(file.getData().getAttr())) {
            try {
                String attr = file.getData().getAttr();
                //Dict dict = JSON.parseObject(attr, Dict.class);
                fileInfo.setAttr(new ObjectMapper().readValue(attr, Dict.class));
            } catch (JsonProcessingException e) {
                fileInfo.setAttr(null);
                log.error("[getByUrl],convert attr fail,file info is:{}",file.getData());
            }
        }

        return fileInfo;
    }

    /**
     * todo:在使用前需要通过set注入fileStorageService
     * 根据名称获取文件信息[接口本身参数应该是 URL，此处使用fileName]
     * @param fileName String文件名
     * @return FileInfo
     */
    public boolean delete(String fileName) {
        String url = basePath + rootPath + fileName;
        ResponseResult<FileDetailPO> file = planetFileApi.getFileByUrl(url);
        if (file == null){
            throw BusinessException.build("删除文件不存在,请检查文件地址是否正确");
        }
        //1、删除磁盘上  2、删除数据库
        fileStorageService.delete(url);
        ResponseResult<Boolean> result = planetFileApi.deleteFileById(file.getData().getId());
        if (result == null || !result.getSuccess() || result.getData() == null){
            throw BusinessException.build("删除文件不存在,请检查文件是否存在[fileId:" +file.getData().getId() + "]");
        }
        return result.getData();
    }

}
